// Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// See page 278.

// Package memo provides a concurrency-safe non-blocking memoization
// of a function.  Requests for different keys proceed in parallel.
// Concurrent requests for the same key block until the first completes.
// This implementation uses a monitor goroutine.
package memo

//!+Func

// Func is the type of the function to memoize.
type Func func(key string) (interface{}, error)

// A result is the result of calling a Func.
type result struct {
	value interface{}
	err   error
}

type entry struct {
	res result
	//用于广播的,因为干活的就一个 需要放结果的多个, 保证活干完就靠这个信号
	ready chan struct{} // closed when res is ready
}

//!-Func

//!+get

// A request is a message requesting that the Func be applied to key.
type request struct {
	key      string
	response chan<- result // the client wants a single result
}

type Memo struct{ requests chan request } //MARK 管道里面传 请求{key,结果传输管道}

// New returns a memoization of f.  Clients must subsequently call Close.
func New(f Func) *Memo {
	memo := &Memo{requests: make(chan request)}
	go memo.server(f) //核心goroutine
	return memo
}

func (memo *Memo) Get(key string) (interface{}, error) {

	//自己新建一个请求 放进去, 保留拿结果的channel
	response := make(chan result)
	//这里放请求 server 里面会处理    ,结果给我放 response里面 (相当于我 交过去一张纸条说要啥 一个框让对方弄好放里面我来取)
	memo.requests <- request{key, response}
	res := <-response //如果没缓存 会阻塞在这 等弄好

	return res.value, res.err
}

func (memo *Memo) Close() { close(memo.requests) }

//!-get

//!+monitor

//MARK 本函数只有main 调用了一次 ,永不结束.里面的行为都很安全
func (memo *Memo) server(f Func) {

	cache := make(map[string]*entry)

	for req := range memo.requests { //会等请求

		e := cache[req.key]
		if e == nil { //第一次来
			e = &entry{ready: make(chan struct{})}
			cache[req.key] = e    //所以活已经有人负责了,即使不一定干完了, e也不会是nil
			go e.call(f, req.key) // call f(key)
		}

		//MARK 一般会走这, 把结果放到  req.response里面
		go e.deliver(req.response)
	}
}

func (e *entry) call(f Func, key string) {
	// Evaluate the function.
	e.res.value, e.res.err = f(key)
	// Broadcast the ready condition.
	close(e.ready) //MARK e里面结果放好后 广播
}

func (e *entry) deliver(response chan<- result) {

	//MARK  阻塞保证e里面的结果放好了,如果e.ready已经close  这里会直接通过
	<-e.ready

	response <- e.res
}

//!-monitor
